﻿using AiMoBridge.Paths;
using AiMoWindows.CodeScripts.IO;
using AiMoWindows.MainActions.Mode;
using System.IO;

namespace AiMoWindows.MainActions.LibraryActions.ExampleItems
{
    static class LibraryExamples
    {
        public static void CreateInitialExamples()
        {
            string outputFolder = PathManager.CustomizedCommandsFolder;
            try
            {
                DirectoryInfo outputDirectory = new DirectoryInfo(outputFolder);
                if (!outputDirectory.Exists) outputDirectory.Create();
                CodeIO.RepairExtensions(outputFolder);
                if (outputDirectory.GetFiles($"*{CodeIO.Extension}").Length == 0)
                {
                    foreach (LibraryExampleItem item in DefaultExamples)
                    {
                        item.WriteToFile(outputDirectory);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show($"cannot create initial example files \r\n {ex.FormatException()}");
            }
        }
        static LibraryExampleItem[] DefaultExamples => new LibraryExampleItem[]
        {
            new LibraryExampleItem(ScriptComplileMode.RhinoScript,
name:"从闭合曲线和层数挤出小房子",
usings:@"using Rhino;
using Rhino.DocObjects;
using Rhino.Geometry;
using System.Collections.Generic;",
mains:@"        // 设定层高
        FloorHeight = 3.5;

        // 查找需要挤出曲线的图层
        Layer curveLayer = RhinoDoc.ActiveDoc.Layers.FindName(""RESA""); // 所有的被挤出的曲线都放在RESA图层中
        if (curveLayer == null)
        {
            Print(""Cannot find curve layer"");
            return;
        }
        // 查找标注挤出高度的图层
        Layer annotationLayer = RhinoDoc.ActiveDoc.Layers.FindName(""ANNOTATION""); // 所有的标注都放在ANNOTATION图层中
        if (annotationLayer == null)
        {
            Print(""Cannot find annotation Layer"");
            return;
        }
        int curveLayerIndex = curveLayer.Index;
        int annotationLayerIndex = annotationLayer.Index;

        // 从图层获取物体
        List<Curve> curves = new List<Curve>();
        List<TextEntity> texts = new List<TextEntity>();
        foreach (RhinoObject obj in RhinoDoc.ActiveDoc.Objects)
        {
            if (obj.Attributes.LayerIndex == curveLayerIndex
              && obj.Geometry is Curve crv
              && crv.IsClosed)
            {
                // 只查找闭合曲线
                curves.Add(crv);
            }
            else if (obj.Attributes.LayerIndex == annotationLayerIndex
              && obj.Geometry is TextEntity textEntity)
            {
                // 只查文字
                texts.Add(textEntity);
            }
        }
        Print($""found { curves.Count} curves"");
        Print($""found { texts.Count} texts"");

        List<Extrusion> extrusions = new List<Extrusion>();
        foreach (Curve crv in curves)
        {
            Extrusion extrusion = ExtrudeCurve(crv, texts);
            if (extrusion != null)
            {
                extrusions.Add(extrusion);
            }
        }
        Print($""extrude { extrusions.Count} extrusions"");

        // Bake with group
        ObjectAttributes attr = new ObjectAttributes();
        int newGroupIndex = RhinoDoc.ActiveDoc.Groups.Add();
        attr.AddToGroup(newGroupIndex);
        foreach (Extrusion ex in extrusions)
        {
            Bake(ex, attr);
        }",
functions:@"    double FloorHeight = 3;
    Extrusion ExtrudeCurve(Curve crv, List<TextEntity> texts)
    {
        foreach (TextEntity text in texts)
        {
            // Found text inside curve
            if (crv.Contains(text.Plane.Origin, Plane.WorldXY, 0.001) == PointContainment.Inside)
            {
                // 查找数字
                if (int.TryParse(text.Text, out int num) && num > 0)
                {

                    // 对方向相反的Curve反转
                    if (crv.ClosedCurveOrientation(Vector3d.ZAxis) == CurveOrientation.Clockwise)
                        crv.Reverse();

                    return Extrusion.Create(crv, FloorHeight * num, true);
                }
            }
        }
        return null;
    }",
descriptions:"从闭合曲线和层数挤出小房子"
                ),

            new LibraryExampleItem(ScriptComplileMode.RhinoScript,
name:"导入CAD改白色",
usings:@"using Rhino;
using Rhino.DocObjects;
using Rhino.Geometry;
using System.Drawing;",
mains:@"        //改变的物体计数
        int objCount = 0;
        BoundingBox bbox = BoundingBox.Empty;

        foreach (RhinoObject robj in RhinoDoc.ActiveDoc.Objects)
        {
            //所有Rhino物体中黑色的
            if (robj.Attributes.ColorSource == ObjectColorSource.ColorFromObject && robj.Attributes.ObjectColor.ToArgb() == Color.Black.ToArgb())
            {
                //将物体改成白色
                robj.Attributes.ObjectColor = Color.White;
                //应用修改
                robj.CommitChanges();
                //改变的物体计数
                objCount++;
            }
            if (robj.Visible) bbox.Union(robj.Geometry.GetBoundingBox(false));
        }

        //打印改变物体数量
        Print(""改变了 {0} 个物体"", objCount);
        //改变的图层计数
        int layerCount = 0;
        foreach (Layer layer in RhinoDoc.ActiveDoc.Layers)
        {
            //所有图层中图层颜色为黑色的
            if (layer.Color.ToArgb() == Color.Black.ToArgb())
            {
                //改成白色
                layer.Color = Color.White;

                //应用图层修改
                RhinoDoc.ActiveDoc.Layers.Modify(layer, layer.Index, true);
                //改变图层计数
                layerCount++;
            }

        }
        //打印改变图层数量
        Print(""改变了 {0} 个图层"", layerCount);
        RhinoDoc.ActiveDoc.Views.ActiveView.ActiveViewport.ZoomBoundingBox(bbox);",
descriptions:"导入CAD改白色"
                ),

            new LibraryExampleItem(ScriptComplileMode.RhinoScript,
name:"去除所有Block嵌套",
usings:@"using Rhino;
using Rhino.DocObjects;
using Rhino.Geometry;
using System.Collections.Generic;",
mains:@"        //选取所有的Block
        if (!Select(out InstanceObject[] instanceObjects)) return;
        //提取所有的对应的Block定义，去除重复
        HashSet<int> definitionIndices = new HashSet<int>();
        foreach (InstanceObject block in instanceObjects)
        {
            definitionIndices.Add(block.InstanceDefinition.Index);
        }
        // 对每一个图块定义都炸开内部嵌套
        foreach (int index in definitionIndices)
        {
            ExplodedBlockDefinition(RhinoDoc.ActiveDoc.InstanceDefinitions[index]);
        }",
functions:@"    void ExplodedBlockDefinition(InstanceDefinition blockDef)
    {
        // 标记是否有嵌套物体，需要修改定义
        bool hasNestedBlock = false;

        // 重新储存的定义物体列表
        List<GeometryBase> geos = new List<GeometryBase>();
        List<ObjectAttributes> attrs = new List<ObjectAttributes>();


        // 对于图块定义里每一个物体
        foreach (RhinoObject obj in blockDef.GetObjects())
        {

            if (obj is InstanceObject instanceObj)
            {
                // 如果是嵌套物体，则标记需要修改定义
                hasNestedBlock = true;

                // 对嵌套物体炸开，并将炸开物体添加到需要修改的定义列表里
                instanceObj.Explode(true, out RhinoObject[] pieces, out ObjectAttributes[] piecesAttrs, out Transform[] trans);
                for (int i = 0; i < pieces.Length; i++)
                {
                    GeometryBase subGeo = pieces[i].DuplicateGeometry();
                    ObjectAttributes subAttr = piecesAttrs[i];
                    // 炸开物体需要应用Block Transform
                    subGeo.Transform(trans[i]);
                    subAttr.Transform(trans[i]);
                    geos.Add(subGeo);
                    attrs.Add(subAttr);
                }
            }
            else
            {
                // 普通物体直接添加
                geos.Add(obj.Geometry);
                attrs.Add(obj.Attributes);
            }
        }

        if (hasNestedBlock)
        {
            // 如果是嵌套Block，进行修改
            RhinoDoc.ActiveDoc.InstanceDefinitions.ModifyGeometry(blockDef.Index, geos, attrs);
        }
    }",
descriptions:"去除所有Block嵌套"
                ),

            new LibraryExampleItem(ScriptComplileMode.RhinoScript,
name:"显示选中的图块名字",
usings:@"using Rhino.DocObjects;",
mains:@"        int nameSize = 30; //显示文字大小
        if (Select(out InstanceObject[] instanceObjects))
        {
            foreach (InstanceObject block in instanceObjects)
            {
                Print(block.InstanceDefinition.Name);
                Show(block.InstanceDefinition.Name, block.Geometry.GetBoundingBox(true).PointAt(0.5, 0.5, 1), nameSize);
            }
        }",
descriptions:"显示选中的图块名字"
                ),

            new LibraryExampleItem(ScriptComplileMode.RhinoScript,
name:"选中内容相同的Block，忽略名字",
usings:@"using Rhino;
using Rhino.DocObjects;
using Rhino.Geometry;
using System.Collections.Generic;
using System.Linq;",
mains:@"        if (Select(out InstanceObject instanceObj))
        {
            string hash = GetHashFromDefinition(instanceObj.InstanceDefinition);
            HashSet<int> hashDefs = new HashSet<int>();
            foreach (InstanceDefinition idef in RhinoDoc.ActiveDoc.InstanceDefinitions)
            {
                if (hash == GetHashFromDefinition(idef))
                {
                    hashDefs.Add(idef.Index);
                }
            }
            foreach (RhinoObject obj in RhinoDoc.ActiveDoc.Objects.GetObjectList(ObjectType.InstanceReference))
            {
                InstanceObject instanceObj2 = obj as InstanceObject;
                if (hashDefs.Contains(instanceObj2.InstanceDefinition.Index))
                {
                    obj.Select(true);
                }
            }
        }
        else
        {
            Print(""No Block Selected"");
        }",
functions:@"    private string GetHashFromDefinition(InstanceDefinition idef)
    {
        List<string> objectHashes = new List<string>();
        foreach (RhinoObject rhinoObject in idef.GetObjects())
        {
            GeometryBase geometry = rhinoObject.Geometry;
            BoundingBox boundingBox = geometry.GetBoundingBox(true);
            double area = boundingBox.Area;
            double width = boundingBox.Diagonal.X;
            double height = boundingBox.Diagonal.Y;
            double depth = boundingBox.Diagonal.Z;
            string objectHash = $""{area:f5}-{width:f5}-{height:f5}-{depth:f5}"";
            objectHashes.Add(objectHash);
        }
        return CalculateCombinedHash(objectHashes);
    }

    private string CalculateCombinedHash(List<string> hashes)
    {
        return string.Join(""x"", Enumerable.OrderBy(hashes, x => x));
    }",
descriptions:"选中内容相同的Block，忽略名字"
                ),

        };



    }
}
